package com.uziot.activiti6.common.util;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author shidt
 * @version V1.0
 * @className ExpressionUtils
 * @date 2020-03-06 12:46
 * @description EL表达式计算取值工具类
 * 通过切面或者给定的参数计算表达式的值，并作为返回对象
 */

public class ExpressionUtils {
    private static ExpressionUtils expressionUtils = null;

    private ExpressionUtils() {
    }

    /**
     * 实例化对象
     *
     * @return 对象
     */
    public static ExpressionUtils instance() {
        if (null == expressionUtils) {
            synchronized (ExpressionUtils.class) {
                expressionUtils = new ExpressionUtils();
            }
        }
        return expressionUtils;
    }

    /**
     * 根据切面信息位置，计算EL表达式的值并加工结果作为返回值
     *
     * @param joinPoint    连接点
     * @param elExpression 表达式
     * @return key
     */
    public String pointEval(JoinPoint joinPoint, String elExpression) {
        // 获取方法入参
        Object[] parameterValues = joinPoint.getArgs();
        // 获取方法形参
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        return this.methodEval(method, parameterValues, elExpression);
    }

    /**
     * @param method          方法名
     * @param parameterValues 参数值列表
     * @param elExpression    表达式
     * @return 结果
     */
    private String methodEval(Method method, Object[] parameterValues, String elExpression) {
        DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
        String[] parameterNames = nameDiscoverer.getParameterNames(method);
        if (parameterNames == null || parameterNames.length == 0) {
            // 方法没有入参,直接返回表达式
            return elExpression;
        }
        return this.expressEvalStr(parameterNames, parameterValues, elExpression);
    }

    /**
     * @param keys         参数名列表
     * @param values       参数值列表
     * @param elExpression 表达式
     * @return 字符串，当解析失败时返回表达式
     */
    public String expressEvalStr(String[] keys, Object[] values, String elExpression) {
        // 解析值列表
        try {
            Object expressionValue = this.expressEval(keys, values, elExpression);
            if (expressionValue != null && !"".equals(expressionValue.toString())) {
                // 返回el解析后的值
                return expressionValue.toString();
            } else {
                // 使用注解上的表达式
                return elExpression;
            }
        } catch (Exception e) {
            // 解析失败，默认使用的表达式
            return elExpression;
        }
    }

    /**
     * 计算表达式的结果返回对象
     *
     * @param keys         keys
     * @param values       values
     * @param elExpression 表达式
     * @return 对象 当返回结果可能为NULL值
     */
    public Object expressEval(String[] keys, Object[] values, String elExpression) {
        // 转换取值表达式
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(elExpression);
        // 通过表达式解析上下文
        EvaluationContext evaluationContext = new StandardEvaluationContext();
        // 给上下文赋值
        for (int i = 0; i < keys.length; i++) {
            evaluationContext.setVariable(keys[i], values[i]);
        }
        // 通过上下文取值返回
        return expression.getValue(evaluationContext);
    }

    /**
     * 计算Bean对象中的字段值
     *
     * @param bean         bean 对象
     * @param elExpression 表达式或字段名
     * @return 解析的对象
     */
    public Object eval(Object bean, String elExpression) {
        ExpressionParser parser = new SpelExpressionParser();
        // 把该表达式，解析成一个Expression对象：SpelExpression
        Expression expression = parser.parseExpression(elExpression);
        return expression.getValue(bean);
    }

    /**
     * 计算mapEval对象中的字段值
     *
     * @param map          map 对象
     * @param elExpression 表达式或字段名
     * @return 解析的对象
     */
    public Object mapEval(Map<String, Object> map, String elExpression) {
        // 转换取值表达式
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(elExpression);
        // 通过表达式解析上下文
        EvaluationContext evaluationContext = new StandardEvaluationContext();
        // 给上下文赋值
        setContextVariable(evaluationContext, map);
        // 通过上下文取值返回
        return expression.getValue(evaluationContext);
    }

    /**
     * 递归设置
     *
     * @param context context
     * @param map     map
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    private void setContextVariable(EvaluationContext context, Map<String, Object> map) {
        map.forEach((k, v) -> {
            if (v instanceof Map) {
                setContextVariable(context, (Map) v);
            } else {
                context.setVariable(k, v);
            }
        });
    }

    public static void main(String[] args) {
        HashMap<String, Object> map = new HashMap<>();
        HashMap<String, Object> oth = new HashMap<>();
        oth.put("days", 100);
        map.put("days", 10);
        map.put("oth", oth);
        Object evalMap = ExpressionUtils.instance().mapEval(map, "#days<10");
        System.out.println(evalMap);

        Object result = FelSupport.result(map, "days<10");
        System.out.println(result);
    }
}
